﻿//**************************************************************
//
// MoneyBaby Project - Open source payment processors for .NET
//
// Copyright 2007-2008 Marcus McConnell and BV Software
// www.CodePlex.com/MoneyBaby
//**************************************************************

using System;
using System.Collections.Generic;
using System.Text;
using System.Web;

namespace BVSoftware.MoneyBaby.Gateways
{
 
    public class LinkPointConnect : BaseCreditCardGateway<LinkPointConnectGatewaySettings>
    {
        internal LinkPointConnect()
        {
        }

        #region  Local Classes

        private class LinkPointResponse
        {
            private string _ApprovalNumber = string.Empty;
            private string _AVSCode = string.Empty;
            private string _ReferenceNumber = string.Empty;
            private string _Status = string.Empty;

            public string ApprovalNumber
            {
                get { return _ApprovalNumber; }
                set { _ApprovalNumber = value; }
            }
            public string AVSCode
            {
                get { return _AVSCode; }
                set { _AVSCode = value; }
            }
            public string ReferenceNumber
            {
                get { return _ReferenceNumber; }
                set { _ReferenceNumber = value; }
            }
            public string Status
            {
                get { return _Status; }
                set { _Status = value; }
            }
        }

        #endregion

        public override Guid GatewayId { get { return new Guid("8CD06872-51C5-4c73-806F-A8A927BE79A9"); } }
        public override string GatewayName { get { return "LinkPoint Connect"; } }

        public override bool SupportsAuthorize { get { return true; } }
        public override bool SupportsCapture { get { return true; } }
        public override bool SupportsCharge { get { return true; } }
        public override bool SupportsRefund { get { return true; } }
        public override bool SupportsVoid { get { return true; } }

        protected override TransactionResultData ProcessCard(PaymentOperationType operationType, PaymentData<LinkPointConnectGatewaySettings> data)
        {
            TransactionResultData result = new TransactionResultData();

            try
            {
                // Get Processor Settings                
                string liveUrl = data.GatewaySettings.UrlLive.ToString();
                string testUrl = data.GatewaySettings.UrlTest.ToString();

                if (liveUrl.Equals(string.Empty))
                {
                    liveUrl = "https://secure.linkpt.net/lpcentral/servlet/lppay";
                }
                if (testUrl.Equals(string.Empty))
                {
                    testUrl = "https://staging.linkpt.net/lpcentral/servlet/lppay";
                }

                // Set Server
                string serverUrl = liveUrl;
                if (data.GatewaySettings.TestMode)
                {
                    serverUrl = testUrl;
                }

                // Build Data String
                // Card Number and Expiration                
                string expMonth = string.Empty;
                // two digit month
                if (data.CardData.ExpirationMonth < 10)
                {
                    expMonth = "0" + data.CardData.ExpirationMonth.ToString();
                }
                else
                {
                    expMonth = data.CardData.ExpirationMonth.ToString();
                }

                // 4 digit year
                string expYear = string.Empty;
                expYear = data.CardData.ExpirationYear.ToString();



                // Set Parameters
                StringBuilder sb = new StringBuilder();
                string postData = string.Empty;

                sb.Append("storename=");
                sb.Append(SafeWriteString(data.GatewaySettings.Username.Trim()));
                sb.Append("&chargetotal=");
                sb.Append(SafeWriteString(data.Transaction.Amount.ToString()));
                sb.Append("&addr=");
                sb.Append(SafeWriteString(data.Customer.AddressLine1));
                sb.Append("&zip=");
                sb.Append(SafeWriteString(data.Customer.PostalCode));
                // This line skips Linkpoint form and responds directly to server;
                sb.Append("&2000=");
                sb.Append(SafeWriteString("Submit"));
                sb.Append("&mode=");
                // Fullpay, Payplus, PayOnly
                sb.Append(SafeWriteString("payonly"));
                sb.Append("&cctype=");
                switch (data.CardData.CardType)
                {
                    case PaymentCardType.Amex:
                        sb.Append("A");
                        break;
                    case PaymentCardType.DinersClub:
                        sb.Append("C");
                        break;
                    case PaymentCardType.Discover:
                        sb.Append("D");
                        break;
                    case PaymentCardType.JCB:
                        sb.Append("J");
                        break;
                    case PaymentCardType.Maestro:
                        sb.Append("X");
                        break;
                    case PaymentCardType.MasterCard:
                        sb.Append("M");
                        break;
                    case PaymentCardType.Other:
                        sb.Append("X");
                        break;
                    case PaymentCardType.Solo:
                        sb.Append("X");
                        break;
                    case PaymentCardType.Switch:
                        sb.Append("X");
                        break;
                    case PaymentCardType.Unknown:
                        sb.Append("X");
                        break;
                    case PaymentCardType.Visa:
                        sb.Append("V");
                        break;
                    default:
                        break;
                }
                sb.Append("&cardnumber=");
                sb.Append(SafeWriteString(data.CardData.CardNumber));
                sb.Append("&expmonth=");
                sb.Append(SafeWriteString(expMonth));
                sb.Append("&expyear=");
                sb.Append(SafeWriteString(expYear));
                if (data.CardData.SecurityCode.Length > 0)
                {
                    sb.Append("&cvm=");
                    sb.Append(SafeWriteString(data.CardData.SecurityCode));
                }
                sb.Append("&oid=");
                sb.Append(SafeWriteString(data.Transaction.MerchantInvoiceNumber));



                switch (operationType)
                {
                    case PaymentOperationType.Charge:                       
                        sb.Append("&txntype=");
                        sb.Append(SafeWriteString("sale"));
                        break;
                    case PaymentOperationType.Authorize:                        
                        sb.Append("&txntype=");
                        sb.Append(SafeWriteString("preauth"));
                        break;
                    case PaymentOperationType.Capture:                        
                        sb.Append("&txntype=");
                        sb.Append(SafeWriteString("postauth"));
                        sb.Append("&refnum=");
                        sb.Append(SafeWriteString(data.Transaction.PreviousTransactionReferenceNumber));
                        break;
                    case PaymentOperationType.Void:                       
                        sb.Append("&txntype=");
                        sb.Append(SafeWriteString("void"));
                        sb.Append("&refnum=");
                        sb.Append(SafeWriteString(data.Transaction.PreviousTransactionReferenceNumber));
                        break;
                    case PaymentOperationType.Refund:                        
                        sb.Append("&txntype=");
                        sb.Append(SafeWriteString("credit"));
                        sb.Append("&refnum=");
                        sb.Append(SafeWriteString(data.Transaction.PreviousTransactionReferenceNumber));
                        break;
                }


                sb.Append("&userid=");
                sb.Append(SafeWriteString(data.Customer.CustomerId));
                sb.Append("&bname=");
                sb.Append(SafeWriteString(data.CardData.CardHolderName));
                sb.Append("&baddr1=");
                sb.Append(SafeWriteString(data.Customer.AddressLine1));
                sb.Append("&bcity=");
                sb.Append(SafeWriteString(data.Customer.City));
                sb.Append("&bcountry=");
                sb.Append(SafeWriteString(data.Customer.Country));

                if (data.Customer.Country.ToLower() == "us")
                {
                    sb.Append("&bstate=");
                    sb.Append(SafeWriteString(data.Customer.State));
                }
                else
                {
                    sb.Append("&bstate2=");
                    sb.Append(SafeWriteString(data.Customer.State));
                }
                sb.Append("&bzip=");
                sb.Append(SafeWriteString(data.Customer.PostalCode));
                sb.Append("&ip=");
                sb.Append(SafeWriteString(data.Customer.IPAddress));

                // retail, mail, telephone, eci (for internet), moto (for virtual checks)
                sb.Append("&txnorg=eci");



                // Dump string builder to string to send to Authorize.Net
                postData = sb.ToString();
                string responseString = Utilities.SendRequestByPost(serverUrl, postData);


                string approvalCode = Utilities.GetInputTagValueFromtHtmlString(responseString, "approval_code");
                string failReason = Utilities.GetInputTagValueFromtHtmlString(responseString, "failReason");
                LinkPointResponse lpResponse = ParseApprovalCode(approvalCode);

                result.ResponseCode = lpResponse.ApprovalNumber;
                result.TransactionReferenceNumber = lpResponse.ReferenceNumber;
                result.AVSCode = lpResponse.AVSCode;

                switch(lpResponse.Status)
                {
                    case "X":
                         result.Suceeded = false;
                        if (failReason.Length > 0)
                        {
                            result.Messages.Add(new TransactionMessage("FAILED: " + failReason, result.ResponseCode, TransactionMessageType.Warning));
                        }
                        else
                        {
                            result.Messages.Add(new TransactionMessage("No transaction response from LinkPoint. Possible invalid transaction.", result.ResponseCode, TransactionMessageType.Warning));
                        }
                        break;
                     case "Y":
                        result.Suceeded = true;
                        result.ResponseCodeDescription = "Tranaction success.";
                        result.Messages.Add(new TransactionMessage("Transaction success.", result.ResponseCode, TransactionMessageType.Information));
                        
                        break;
                     case "D":
                        result.Suceeded = false;
                        result.ResponseCodeDescription = "Tranaction declined.";
                        result.Messages.Add(new TransactionMessage("Transaction declined.", result.ResponseCode, TransactionMessageType.Warning));
                        break;
                    case "R":
                        result.Suceeded = false;
                        result.ResponseCodeDescription = "Tranaction declined. Voice referral requested.";
                        result.Messages.Add(new TransactionMessage("Transaction declined. Voice referral requested.", result.ResponseCode, TransactionMessageType.Warning));
                        break;
                    default:
                        result.Suceeded = false;
                        result.ResponseCodeDescription = "Unknown error: " + failReason;
                        result.Messages.Add(new TransactionMessage("Unknown error: " + failReason, result.ResponseCode, TransactionMessageType.Error));
                        break;
                }

                result.AVSCodeDescription = ParseAvsCode(lpResponse.AVSCode);
                              
            }
            catch (Exception ex)
            {
                result.Suceeded = false;
                result.Messages.Add(new TransactionMessage("Unknown Payment Error: " + ex.Message, "MBAN1001", TransactionMessageType.Error));
                result.Messages.Add(new TransactionMessage("Stack Trace " + ex.StackTrace, "STACKTRACE", TransactionMessageType.Information));
            }
            return result;
        }

     
        private string SafeWriteString(string input)
        {
            return HttpUtility.UrlEncode(input);
        }

        private string ParseAvsCode(string code)
        {
            string result = "Uknown";

            switch (code.ToUpper())
            {

                case "YY":
                    result = " [AVS - Address matches, zip code matches]";
                    break;
                case "YN":
                    result = " [AVS - Address matches, zip code does not match]";
                    break;
                case "YX":
                    result = " [AVS - Address matches, zip code comparison not available]";
                    break;
                case "NY":
                    result = " [AVS - Address does not match, zip code matches]";
                    break;
                case "XY":
                    result = " [AVS - Address comparison not available, zip code matches]";
                    break;
                case "NN":
                    result = " [AVS - Address comparison does not match, zip code does not match]";
                    break;
                case "NX":
                    result = " [AVS - Address does not match, zip code comparison not available]";
                    break;
                case "XN":
                    result = " [AVS - Address comparison not available, zip code does not mat]";
                    break;
                case "XX":
                    result = " [AVS - Address comparisons not available, zip code comparison not available]";
                    break;
                default:
                    result = " [AVS - Unknown Response Code]";
                    break;
            }

            return result;
        }

        private LinkPointResponse ParseApprovalCode(string code)
        {
            LinkPointResponse result = new LinkPointResponse();
            result.Status = "X";
            
            if (code.Length > 0)
            {
                result.Status = code.Substring(0,1);
            }

            char[] codeArray = code.ToCharArray();

                    int colonCount = 0;
                    string refString = string.Empty;

            switch (result.Status.ToUpper())
            {
                case "Y":
                    for (int i = 0; i < codeArray.Length -1;i++)
                    {
                        switch(colonCount)
                        {
                            case 1:
                                // add to refString
                                if (codeArray[i] != ':')
                                {
                                    refString += codeArray[i];
                                }
                                break;
                            case 2:
                                // add to AVS
                                if (codeArray[i] != ':')
                                {
                                    result.AVSCode += codeArray[i];
                                }
                                break;
                            default:
                                // do nothing
                                break;
                        }

                        if (codeArray[1] == ':')
                        {
                            colonCount += 1;
                        }
                    }

                    if (refString.Length >= 6)
                    {
                        result.ApprovalNumber = refString.Substring(0,6);
                    }
                    
                    if (refString.Length >= 16)
                    {
                        result.ReferenceNumber = refString.Substring(6, 10);
                    }
                               

                    break;
                case "D":
                    for (int i = 0; i < codeArray.Length -1;i++)
                    {
                        switch(colonCount)
                        {
                            case 1:
                                // add to refString
                                if (codeArray[i] != ':')
                                {
                                    refString += codeArray[i];
                                }
                                break;
                            case 2:
                                // add to AVS
                                if (codeArray[i] != ':')
                                {
                                    result.AVSCode += codeArray[i];
                                }
                                break;
                            default:
                                // do nothing
                                break;
                        }

                        if (codeArray[1] == ':')
                        {
                            colonCount += 1;
                        }
                    }
                    break;
                case "R":
                    // do nothing
                    break;
                default:
                    // do nothing
                    break;

            }


            return result;
        }    
    }
}
